// 图标选择组件 FormComponent

import React, { createContext, CSSProperties, Fragment, useContext, useEffect, useState } from 'react';
import { FormComponentType } from '@/types/component-type';
import { IconFont, IconFontProps } from '@/components/IconFont';
import * as _antIcons from '@ant-design/icons';
import { CloseCircleOutlined, FieldBinaryOutlined, InfoCircleOutlined, SearchOutlined } from '@ant-design/icons';
import { DraggableModal } from '@/components/DraggableModal';
import { Badge, Card, Divider, Flex, Input, Space, Tabs, Tooltip } from 'antd';
import * as _iconfont from '@/assets/iconfont/iconfont.json';

declare type IconType = 'Filled' // 实底风格
  | 'TwoTone' // 双色风格
  | 'Outlined' // 线框风格
  | 'None'; // 扩展图标

const supportIconTypes: IconType[] = [ 'Outlined', 'Filled', 'TwoTone', 'None' ];

declare type GlyphType = {
  icon_id?: string;
  name?: string;
  font_class?: string;
  unicode?: string;
  unicode_decimal?: string;
}

/**
 * <a href='https://www.iconfont.cn/'>iconfont</a>
 */
declare type IconFontType = {
  id?: string;
  name?: string;
  font_family?: string;
  css_prefix_text?: string;
  description?: string;
  glyphs?: GlyphType[];
}

interface IconProps {
  id?: string;
  name?: string;
  type?: IconType;
  render?: (props?: IconFontProps) => React.ReactNode;
  // 已经渲染出的节点，懒加载使用
  node?: React.ReactNode;
  // 图标描述
  describes?: string[];
  props?: any;
}

class Loader {
  private readonly list: IconProps[] = [];
  // 是否支持懒加载
  private readonly lazy: boolean = false;
  private readonly defaultValue: React.ReactNode = <FieldBinaryOutlined />;

  constructor(lazy?: boolean, defaultValue?: React.ReactNode) {
    if (lazy) this.lazy = lazy;
    if (defaultValue) this.defaultValue = defaultValue;

    const antIcons: { [key: string]: { render: IconProps['render'] } } = _antIcons as any;

    const idGenerator = (type: string, key: string) => {
      const id = key.replace(/([A-Z])/g, '-$1').toLowerCase()
        .replace(/^-|-$/g, '');
      return `${ type }:${ id }`;
    };

    const getType = (key: string): IconType => supportIconTypes.find(i => key.length > i.length && key.length - i.length === key.lastIndexOf(i)) || 'None';

    // 处理 Ant Design Icons
    Object.keys(antIcons).forEach(key => {
      // default 对应的 render 函数有问题
      if (key === 'default') return;
      const attr = antIcons[key] || {};
      const { render } = attr;
      if (render && typeof render === 'function') {
        const id = idGenerator('ant-design', key);
        // 此处不适合挂在 Node ，未知错误
        const item: IconProps = {
          id,
          name: key,
          type: getType(key),
          render,
          describes: [ id, key ],
          props: attr,
        };
        this.list.push(item);
      } else {
        // logger.warn('没有找到 render 函数：' + key)
      }
    });

    // 处理扩展图标
    const iconfont: IconFontType = _iconfont as any;
    if (iconfont && iconfont.glyphs) {
      iconfont.glyphs.forEach((glyph: GlyphType) => {
        const { name, font_class } = glyph;
        const key = `${ iconfont?.css_prefix_text }${ font_class }`;
        const id = idGenerator('iconfont', key);
        const render = (props?: IconFontProps) => <IconFont type={ key } { ... props } />;
        const node = <IconFont type={ key } />;
        const item: IconProps = {
          id,
          name,
          type: 'None',
          render,
          node,
          describes: [ id, key ],
          props: { ... glyph, type: key },
        };
        this.list.push(item);
      });
    }
  }

  public render(item: IconProps, props?: IconFontProps): React.ReactNode {
    try {
      const { render, node } = item;
      if (node && !props && this.lazy) {
        return node;
      }
      if (render && typeof render === 'function') {
        item.node = render(props);
        return item.node;
      }
      return this.defaultValue;
    } catch (e) {
      // logger.error('渲染图标出错：', item);
    }
  }

  public find(id: IconFontProps['id'], props?: IconFontProps): React.ReactNode {
    const type = this.list.find(i => id === i.id);
    if (type) {
      return this.render(type, props);
    }

    return this.defaultValue;
  }

  public loader(): IconProps[] {
    this.list.forEach(item => this.render(item));

    return this.list;
  }
}

const loader = new Loader(true);

/// components ---------------------------------------------------------------------------------------------------------

interface BoxWrapperProps extends IconProps {
  key?: React.Key,
  setSelected?: (selected?: string) => void;
}

const BoxWrapperContext = createContext(null);

const BoxWrapper: React.FC<BoxWrapperProps> = (props) => {
  const { id, name, node, setSelected, key } = props;
  const [ hovered, setHovered ] = useState<boolean>(false);
  const selected = useContext(BoxWrapperContext);

  const style: CSSProperties = {
    border: hovered || selected === id ? 'solid 1px blue' : 'solid 1px #e5e7eb',
    marginTop: 4,
    marginRight: 4,
    width: 80,
    textAlign: 'center',
    padding: 8,
    cursor: 'pointer',
  };

  const render = () => {
    try {
      if (node) {
        return node;
      } else if (props.render && typeof props.render === 'function') {
        return props.render(props?.props);
      }
    } catch (e) {
    }
  };

  return <div
    key={ key }
    title={ name }
    style={ style }
    onMouseEnter={ () => setHovered(true) }
    onMouseLeave={ () => setHovered(false) }
    onClick={
      () => {
        if (typeof setSelected === 'function') {
          setSelected(id);
        }
      }
    }
  >
    { render() }
  </div>;
};

declare type IconFontSelectProps = FormComponentType

const IconFontSelect: React.FC<IconFontSelectProps> = (props) => {
  const { value, onChange } = props;

  const list: IconProps[] = loader.loader();
  const map: { [key in IconType]: string } =
    { 'Outlined': '线框风格', 'Filled': '实底风格', 'TwoTone': '双色风格', 'None': '扩展图标' };

  const [ visible, setVisible ] = useState<boolean>(false);
  const [ selected, setSelected ] = useState<IconProps['id'] | any>(value);
  const [ filterIcons, setFilterIcons ] = useState<IconProps[]>(list);

  useEffect(() => {
    if (onChange && typeof onChange === 'function') {
      onChange(selected);
    }
  }, [ selected ]);

  useEffect(() => setFilterIcons(list), [ visible ]);

  const search = (val?: string) => {
    const key = `${ val }`.replaceAll(' ', '');
    const types = list.filter(type => (type?.describes || []).filter(str => `${ str }`.includes(key)).length > 0);
    setFilterIcons(types);
  };

  const render = (type: IconType, types: IconProps[]) => {
    return <Flex key={ type } justify="flex-start" align="center" style={ { flexWrap: 'wrap' } }>
      { types.map(item => <BoxWrapper key={ item?.id } { ... item } setSelected={ setSelected } />) }
    </Flex>;
  };

  return <Fragment>
    <BoxWrapperContext.Provider value={ selected }>
      <Input
        value={ selected }
        onChange={ (val) => setSelected(val as any) }
        onClick={ () => setVisible(true) }
        placeholder="请选择图标"
        addonBefore={
          <Tooltip title="点击选择" placement="right">
              <span
                style={ { cursor: 'pointer' } }
                onClick={ () => setVisible(true) }
              >
                  { loader.find(selected || 'default') }
              </span>
          </Tooltip>
        }
        addonAfter={
          <Tooltip title="清空选择" placement="left">
              <span
                style={ { cursor: 'pointer' } }
                onClick={ () => setSelected(null) }
              >
                  <CloseCircleOutlined />
              </span>
          </Tooltip>
        }
        readOnly allowClear
      />
      <DraggableModal
        title={
          <Space>
            图标选择
            { selected ? <><Divider type="vertical" />{ loader.find(selected) } { selected }</> : null }
          </Space>
        }
        open={ visible } onCancel={ () => setVisible(false) }
        width={ document.body.clientWidth * 0.618 }
        footer={ false }
      >
        <Flex vertical>
          <Card
            title={
              <Input
                placeholder="搜索图标"
                prefix={ <SearchOutlined /> }
                suffix={ <Tooltip title="输入搜索关键字搜索图标"><InfoCircleOutlined /></Tooltip> }
                allowClear
                onChange={ e => search(e.target.value) }
              />
            }
          >
            <Flex vertical>
              <Tabs
                defaultActiveKey="Outlined" tabPosition="left"
                items={
                  supportIconTypes.map(type => {
                    const types = filterIcons.filter(item => item.type === type);
                    return {
                      key: type,
                      label: <Badge
                        count={ types.length }
                        overflowCount={ 1000 }
                        styles={
                          { indicator: { opacity: 0.7, zIndex: -1 } }
                        }
                      >
                        { map[type] }
                      </Badge>,
                      children: render(type, types),
                      style: { height: 300, overflow: 'auto' },
                      forceRender: true,
                    };
                  })
                }
              />
            </Flex>
          </Card>
        </Flex>
      </DraggableModal>
    </BoxWrapperContext.Provider>
  </Fragment>;
};

export {
  IconFontSelectProps,
  IconFontSelect,
  loader, Loader,
};